﻿using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace TiktokAcquisition
{
    public class ElasticsearchClient : IDisposable
    {
        private readonly HttpClient _httpClient;
        private readonly string _baseUrl;
        private readonly JsonSerializerSettings _jsonSettings;

        /// <summary>
        /// 初始化Elasticsearch客户端
        /// </summary>
        /// <param name="baseUrl">ES基础地址，如：http://localhost:9200</param>
        /// <param name="timeoutSeconds">超时时间(秒)</param>
        public ElasticsearchClient(string baseUrl, int timeoutSeconds = 30)
        {
            _baseUrl = baseUrl.TrimEnd('/');
            _httpClient = new HttpClient
            {
                Timeout = TimeSpan.FromSeconds(timeoutSeconds)
            };

            _jsonSettings = new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver(),
                NullValueHandling = NullValueHandling.Ignore,
                DateTimeZoneHandling = DateTimeZoneHandling.Utc
            };
        }

        /// <summary>
        /// 执行搜索查询
        /// </summary>
        /// <param name="indexName">索引名称</param>
        /// <param name="query">查询对象</param>
        /// <returns>原始响应JSON字符串</returns>
        public async Task<string> SearchAsync(string indexName, object query)
        {
            var requestUri = $"{_baseUrl}/{indexName}/_search?preference=primary";
            string queryJson = JsonConvert.SerializeObject(query, _jsonSettings);
            var content = new StringContent(queryJson, Encoding.UTF8, "application/json");

            var response = await _httpClient.PostAsync(requestUri, content);
            response.EnsureSuccessStatusCode();

            return await response.Content.ReadAsStringAsync();
        }

        /// <summary>
        /// 执行搜索查询并反序列化为指定类型
        /// </summary>
        /// <typeparam name="T">返回结果类型</typeparam>
        /// <param name="indexName">索引名称</param>
        /// <param name="query">查询对象</param>
        /// <returns>反序列化后的结果</returns>
        public async Task<ElasticsearchResponse<T>> SearchAsync<T>(string indexName, object query)
        {
            var json = await SearchAsync(indexName, query);
            return JsonConvert.DeserializeObject<ElasticsearchResponse<T>>(json, _jsonSettings);
        }

        /// <summary>
        /// 批量获取文档
        /// </summary>
        /// <param name="indexName">索引名称</param>
        /// <param name="ids">文档ID集合</param>
        /// <returns>原始响应JSON字符串</returns>
        public async Task<string> MultiGetAsync(string indexName, IEnumerable<string> ids)
        {
            var requestUri = $"{_baseUrl}/{indexName}/_mget";

            // 构建mget请求体
            var requestBody = new
            {
                ids = ids
            };

            string requestJson = JsonConvert.SerializeObject(requestBody, _jsonSettings);
            var content = new StringContent(requestJson, Encoding.UTF8, "application/json");

            var response = await _httpClient.PostAsync(requestUri, content);
            response.EnsureSuccessStatusCode();

            return await response.Content.ReadAsStringAsync();
        }

        /// <summary>
        /// 批量获取文档并反序列化为指定类型
        /// </summary>
        /// <typeparam name="T">文档类型</typeparam>
        /// <param name="indexName">索引名称</param>
        /// <param name="ids">文档ID集合</param>
        /// <returns>反序列化后的结果</returns>
        public async Task<MultiGetResponse<T>> MultiGetFanDetailsAsync<T>(string indexName, IEnumerable<string> ids)
        {
            var json = await MultiGetAsync(indexName, ids);
            return JsonConvert.DeserializeObject<MultiGetResponse<T>>(json, _jsonSettings);
        }

        /// <summary>
        /// 插入或更新文档（返回原始JSON字符串）
        /// </summary>
        public async Task<string> IndexDocumentAsync(string indexName, string id, object document)
        {
            // 参数检查
            if (string.IsNullOrWhiteSpace(indexName))
                throw new ArgumentException("索引名称不能为空", nameof(indexName));
            if (string.IsNullOrWhiteSpace(id))
                throw new ArgumentException("文档ID不能为空", nameof(id));
            if (document == null)
                throw new ArgumentNullException(nameof(document));

            // 构建请求URI
            var requestUri = $"{_baseUrl}/{indexName}/_doc/{Uri.EscapeDataString(id)}";

            // 序列化文档
            string documentJson = JsonConvert.SerializeObject(document, _jsonSettings);
            var content = new StringContent(documentJson, Encoding.UTF8, "application/json");

            // 执行请求
            var response = await _httpClient.PutAsync(requestUri, content);
            response.EnsureSuccessStatusCode();

            return await response.Content.ReadAsStringAsync();
        }



        public void Dispose()
        {
            _httpClient?.Dispose();
        }
    }

    /// <summary>
    /// 批量获取文档的响应结构
    /// </summary>
    public class MultiGetResponse<T>
    {
        [JsonProperty("docs")]
        public List<MultiGetHit<T>> Docs { get; set; }
    }

    public class MultiGetHit<T>
    {
        [JsonProperty("_index")]
        public string Index { get; set; }

        [JsonProperty("_type")]
        public string Type { get; set; }

        [JsonProperty("_id")]
        public string Id { get; set; }

        [JsonProperty("found")]
        public bool Found { get; set; }

        [JsonProperty("_source")]
        public T Source { get; set; }
    }
    /// <summary>
    /// Elasticsearch响应结构
    /// </summary>
    public class ElasticsearchResponse<T>
    {
        [JsonProperty("took")]
        public int Took { get; set; }

        [JsonProperty("timed_out")]
        public bool TimedOut { get; set; }

        [JsonProperty("_shards")]
        public Shards Shards { get; set; }

        [JsonProperty("hits")]
        public Hits<T> Hits { get; set; }
    }

    public class Shards
    {
        [JsonProperty("total")]
        public int Total { get; set; }

        [JsonProperty("successful")]
        public int Successful { get; set; }

        [JsonProperty("skipped")]
        public int Skipped { get; set; }

        [JsonProperty("failed")]
        public int Failed { get; set; }
    }

    public class Hits<T>
    {
        [JsonProperty("total")]
        public Total Total { get; set; }

        [JsonProperty("max_score")]
        public double? MaxScore { get; set; }

        [JsonProperty("hits")]
        public List<Hit<T>> Items { get; set; }
    }

    public class Total
    {
        [JsonProperty("value")]
        public int Value { get; set; }

        [JsonProperty("relation")]
        public string Relation { get; set; }
    }

    public class Hit<T>
    {
        [JsonProperty("_index")]
        public string Index { get; set; }

        [JsonProperty("_type")]
        public string Type { get; set; }

        [JsonProperty("_id")]
        public string Id { get; set; }

        [JsonProperty("_score")]
        public double? Score { get; set; }

        [JsonProperty("_source")]
        public T Source { get; set; }
    }
}

